
#include "UhDev.h"

void UhDev_EnumError(UhDev* pUhDev, int status)
{
	#ifdef 0
	if (status == UHC_ENUM_DISCONNECT) 
	{
		uhc_enum_try = 0;
		return; // Abort enumeration process
	}
	uhd_ep_free(uhc_dev_enum->address, 0xFF);

	// Free USB configuration descriptor buffer
	if (uhc_dev_enum->conf_desc != NULL) 
	{
		free(uhc_dev_enum->conf_desc);
		uhc_dev_enum->conf_desc = NULL;
	}
	
	uhc_dev_enum->address = 0;
	if (uhc_enum_try++ < UHC_ENUM_NB_TRY)
	{
		// Restart enumeration at beginning
		uhc_enumeration_step1();
		return;
	}
	// Abort enumeration, set line in suspend mode
	//uhc_enumeration_suspend();
	//UHC_ENUM_EVENT(uhc_dev_enum, status);
	uhc_enum_try = 0;
	#endif
}

static usb_setup_req_t* UhDev_BuildReq(uint8 bmRequestType
	, uint8 bRequest
	, uint16 wValue
	, uint16 wIndex
	, uint16 wLength
	)
{
	static usb_setup_req_t req;

	req.bmRequestType = bmRequestType;
	req.bRequest = bRequest;

	req.wValue = wValue;

	req.wIndex = wIndex;
	req.wLength = wLength;

	return &req;
}

void UhDev_EnumSetConfigDone(UhDev* pUhDev, SetupReq* pSetup, uint16 nPayloadSize, UsbStatus status)
{
	if(status == URB_STATUS_SUCCESS)
	{
		BlkInPipe_Init(&pUhDev->m_BlkInPipe)
	}
	else
	{
		UhDev_EnumError(pUhDev, UHC_ENUM_FAIL);
	}
}

void UhDev_EnumGetConfigDescDone(UhDev* pUhDev, SetupReq* pSetup, uint16 nPayloadSize, UsbStatus status)
{
	usb_setup_req_t* pReq = Null;
	usb_conf_desc_t* pDesc = (usb_conf_desc_t*)&pUhDev->conf_desc;

	pDesc->wTotalLength = N2H16(pDesc->wTotalLength);

	Assert(pDesc->wTotalLengt <= MAX_CONFIG_DESC_SIZE);
	
	if(status == URB_STATUS_SUCCESS && pDesc->bDescriptorType == USB_DT_CONFIGURATION)
	{
		if(sizeof(usb_conf_desc_t) == nPayloadSize)
		{
			//Requests a complete Get configuration descriptor.
			pReq = UhDev_BuildReq(USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN
				, USB_REQ_GET_DESCRIPTOR
				, (USB_DT_CONFIGURATION << 8) | (pDesc->bConfigurationValue - 1)
				, 0
				, pDesc->wTotalLength
				);
			
			if(!CtrlPipe_TxSetupReq(&pUhDev->m_CtrlPipe, pReq, &pUhDev->conf_desc, pReq->wLength, (TransDoneFun)UhDev_EnumGetConfigDescDone, pUhDev))
			{
				UhDev_EnumError(pUhDev, UHC_ENUM_HARDWARE_LIMIT);
			}
		}
		else
		{
			//Enable USB configuration, if unless one USB interface is supported by UHIs.
			pReq = UhDev_BuildReq(USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_OUT
				, USB_REQ_SET_CONFIGURATION
				, pDesc->bConfigurationValue
				, 0
				, 0
				);
			if(!CtrlPipe_TxSetupReq(&pUhDev->m_CtrlPipe, pReq, Null, pReq->wLength, (TransDoneFun)UhDev_EnumSetConfigDone, pUhDev))
			{
				UhDev_EnumError(pUhDev, UHC_ENUM_HARDWARE_LIMIT);
			}
		}
	}
	else
	{
		UhDev_EnumError(pUhDev, UHC_ENUM_FAIL);
	}
}

void UhDev_EnumSetDevAddrDone(UhDev* pUhDev, SetupReq* pSetup, uint16 nPayloadSize, UsbStatus status)
{
	
	if(status == URB_STATUS_SUCCESS)
	{
		pUhDev->m_State = HOST_STATE_SETADDR;
		// Wait 20ms
		Arch_TimerStart(SETADDR_TIMER_ID, 20, (MsgIf*)pUhDev);
	}
	else
	{
		UhDev_EnumError(pUhDev, UHC_ENUM_FAIL);
	}
}

void UhDev_EnumGetDeviceDescDone(UhDev* pUhDev, SetupReq* pSetup, uint16 nPayloadSize, UsbStatus status)
{
	usb_setup_req_t* pReq = Null;
	
	if(status == URB_STATUS_SUCCESS)
	{
		if(HOST_STATE_INIT == pUhDev->m_State)
		{
			//Send a Set address setup request.
			pReq = UhDev_BuildReq(USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_OUT
				, USB_REQ_SET_ADDRESS
				, UHC_DEVICE_ENUM_ADD
				, 0
				, 0
				);
			if(!CtrlPipe_TxSetupReq(&pUhDev->m_CtrlPipe, pReq, Null, 0, (TransDoneFun)UhDev_EnumSetDevAddrDone, pUhDev))
			{
				UhDev_EnumError(pUhDev, UHC_ENUM_HARDWARE_LIMIT);
			}
		}
		else
		{
			//Requests the first USB structure of the USB configuration descriptor.
			pReq = UhDev_BuildReq(USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN
				, USB_REQ_GET_DESCRIPTOR
				, (USB_DT_CONFIGURATION << 8)
				, 0
				, sizeof(usb_conf_desc_t)
				);
			
			if(!CtrlPipe_TxSetupReq(&pUhDev->m_CtrlPipe, pReq, &pUhDev->conf_desc, pReq->wLength, (TransDoneFun)UhDev_EnumGetConfigDescDone, pUhDev))
			{
				UhDev_EnumError(pUhDev, UHC_ENUM_HARDWARE_LIMIT);
			}
		}

	}
	else
	{
		UhDev_EnumError(pUhDev, UHC_ENUM_FAIL);
	}
}

void UhDev_EnumGetDeviceDesc(UhDev* pUhDev)
{
	usb_setup_req_t* pReq = UhDev_BuildReq(USB_REQ_RECIP_DEVICE|USB_REQ_TYPE_STANDARD|USB_REQ_DIR_IN
		, USB_REQ_GET_DESCRIPTOR
		, (USB_DT_DEVICE << 8)
		, 0
		, sizeof(usb_dev_desc_t)
		);

	if(!CtrlPipe_TxSetupReq(&pUhDev->m_CtrlPipe, pReq, &pUhDev->dev_desc, pReq->wLength, (TransDoneFun)UhDev_EnumGetDeviceDescDone, pUhDev))
	{
		UhDev_EnumError(pUhDev, UHC_ENUM_HARDWARE_LIMIT);
	}
}

void UhDev_MsgConnect(UhDev* pUhDev, uint32 speed, uint32 param2)
{
	pUhDev->speed = (uhd_speed)speed;

	Arch_TimerStart(CONNECT_TIMER_ID, 100, (MsgIf*)pUhDev);
}

void UhDev_MsgTimeOut(UhDev* pUhDev, uint32 timeId, uint32 param2)
{
	switch(timeId)
	{
		case BUS_RESET_TIMER_ID:
			break;
		case CONNECT_TIMER_ID:
			UhDev_EnumGetDeviceDesc(pUhDev);
			break;
		case SETADDR_TIMER_ID:
			UhDev_EnumGetDeviceDesc(pUhDev);
			break;
	}

}

int UhDev_MsgProc(UhDev* pUhDev, uint32 msgID, uint32 param1, uint32 param2)
{
	const MsgMap g_MsgTbl[] = 
	{
		{0, (MsgMapFun)UhDev_MsgConnect};
	};
	const static int nCount = sizeof(g_MsgTbl)/MsgMap;
	
	return MsgIf_MsgProc((MsgIf*)pUhDev, msgID, param1, param2, g_MsgTbl, nCount);
}

void UhDev_Start(UhDev* pUhDev)
{
	//uhc_enumeration_reset();
	UsbHost_BusReset(pUhDev);
	Arch_TimerStart(BUS_RESET_TIMER_ID, 20, (MsgIf*)pUhDev);
}

void UhDev_init(UhDev* pUhDev)
{
	memset(pUhDev, 0, sizeof(UhDev));

	pUhDev->m_State = pUhDev;
}

